﻿using System;
using System.Collections.ObjectModel;

namespace Mono.Cecil {

	public sealed class GenericParameterCollection : Collection<GenericParameter> {}

	public sealed class GenericArgumentCollection : Collection<TypeReference> { }

	public sealed class GenericConstraintCollection : Collection<TypeReference> { }

	public sealed class InterfaceCollection : Collection<TypeReference> { }

	public sealed class OverrideCollection : Collection<MethodReference> { }

	public sealed class FieldDefinitionCollection : MemberDefinitionCollection<FieldDefinition> {

		public FieldDefinitionCollection (TypeDefinition container)
			: base (container)
		{
		}
	}

	public sealed class MethodDefinitionCollection : MemberDefinitionCollection<MethodDefinition> {

		public MethodDefinitionCollection (TypeDefinition container)
			: base (container)
		{
		}
	}

	public sealed class EventDefinitionCollection : MemberDefinitionCollection<EventDefinition> {

		public EventDefinitionCollection (TypeDefinition container)
			: base (container)
		{
		}
	}

	public sealed class PropertyDefinitionCollection : MemberDefinitionCollection<PropertyDefinition> {

		public PropertyDefinitionCollection (TypeDefinition container)
			: base (container)
		{
		}
	}

	public sealed class NestedTypeCollection : MemberDefinitionCollection<TypeDefinition> {

		public NestedTypeCollection (TypeDefinition container)
			: base (container)
		{
		}
	}

	public abstract class MemberDefinitionCollection<TElement>
		: Collection<TElement> where TElement : IMemberDefinition {

		TypeDefinition container;

		public MemberDefinitionCollection (TypeDefinition container)
		{
			this.container = container;
		}

		protected sealed override void SetItem (int index, TElement item)
		{
			Attach (item);

			base.SetItem (index, item);
		}

		protected sealed override void InsertItem (int index, TElement item)
		{
			Attach (item);

			base.InsertItem (index, item);
		}

		protected sealed override void RemoveItem (int index)
		{
			Detach (this [index]);

			base.RemoveItem (index);
		}

		protected sealed override void ClearItems ()
		{
			foreach (var definition in this)
				Detach (definition);

			base.ClearItems ();
		}

		void Attach (TElement element)
		{
			if (element.DeclaringType != null)
				throw new ArgumentException ("Member already attached, clone it instead");

			element.DeclaringType = this.container;
		}

		void Detach (TElement element)
		{
			element.DeclaringType = null;
		}
	}
}
